<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Transformations</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="PyCairo, graphics, Python, 2D vector library, 
Transformations, matrix">
<meta name="description" content="In this chapter of the PyCairo tutorial, we will
do some transformations.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container2">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content2">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Transformations</h1>


<p>
In this part of the PyCairo graphics programming tutorial, we will 
talk about transformations.
</p>

<div class="big_hor">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* big_horizontal */
google_ad_slot = "2904953388";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p>
<b>An affine transform</b> is composed of zero or more linear transformations 
(rotation, scaling or shear) and translation (shift). Several linear transformations 
can be combined into a single matrix.
A <b>rotation</b> is a transformation that moves a rigid body around a fixed point.
A <b>scaling</b> is a transformation that enlarges or diminishes objects. 
The scale factor is the same in all directions.
A <b>translation</b> is a transformation that moves every point a constant 
distance in a specified direction.
A <b>shear</b> is a transformation that moves an object perpendicular to a
given axis, with greater value on one side of the axis than the other.
</p>

<p>
sources: (wikipedia.org, freedictionary.com)
</p>


<h2>Translation</h2>

<p>
The following example describes a simple translation.
</p>

<pre class="code">
def on_draw(self, wid, cr):

    cr.set_source_rgb(0.2, 0.3, 0.8)
    cr.rectangle(10, 10, 30, 30)
    cr.fill()
    
    cr.translate(20, 20)
    cr.set_source_rgb(0.8, 0.3, 0.2)
    cr.rectangle(0, 0, 30, 30)
    cr.fill()
    
    cr.translate(30, 30)
    cr.set_source_rgb(0.8, 0.8, 0.2)
    cr.rectangle(0, 0, 30, 30)
    cr.fill()    
    
    cr.translate(40, 40)
    cr.set_source_rgb(0.3, 0.8, 0.8)
    cr.rectangle(0, 0, 30, 30)
    cr.fill()                
</pre>

<p>
The example draws a rectangle. Then we do a translation and 
draw the same rectangle again a few times.
</p>

<pre class="explanation">
cr.translate(20, 20)
</pre>

<p>
The <code>translate()</code> function modifies the current transformation 
matrix by translating the user space origin. In our case we shift the origin 
by 20 units in both directions.
</p>


<img src="/img/gfx/pycairo/translation.png" alt="Translation operation">
<div class="figure">Figure: Translation operation</div>


<h2>Shearing</h2>

<p>
In the following example, we perform a shearing operation.
A shearing is an object distortion along a particular axis.
There is no shear method for this operation. We need to create
our own transformation matrix. Note that each affine transformation
can be performed by creating a transformation matrix.
</p>


<pre class="code">
def on_draw(self, wid, cr):

    cr.set_source_rgb(0.6, 0.6, 0.6)
    cr.rectangle(20, 30, 80, 50)
    cr.fill()

    mtx = cairo.Matrix(1.0, 0.5,
                0.0, 1.0,
                0.0, 0.0)

    cr.transform(mtx)
    cr.rectangle(130, 30, 80, 50)
    cr.fill()             
</pre>

<p>
In this code example, we perform a simple shearing operation.
</p>

<pre class="explanation">
mtx = cairo.Matrix(1.0, 0.5,
            0.0, 1.0,
            0.0, 0.0)
</pre>

<p>
This transformation shears y values by 0.5 of the x values.
</p>

<pre class="explanation">
cr.transform(mtx)
</pre>

<p>
We perform the transformation with the <code>transform()</code>
method.
</p>


<img src="/img/gfx/pycairo/shearing.png" alt="Shearing operation">
<div class="figure">Figure: Shearing operation</div>


<h2>Scaling</h2>

<p>
The next example demonstrates a scaling operation. Scaling is a transformation
operation where the object is enlarged or shrinken.
</p>

<pre class="code">
def on_draw(self, wid, cr):

    cr.set_source_rgb(0.2, 0.3, 0.8)
    cr.rectangle(10, 10, 90, 90)
    cr.fill()
    
    cr.scale(0.6, 0.6)
    cr.set_source_rgb(0.8, 0.3, 0.2)
    cr.rectangle(30, 30, 90, 90)
    cr.fill()
    
    cr.scale(0.8, 0.8)
    cr.set_source_rgb(0.8, 0.8, 0.2)
    cr.rectangle(50, 50, 90, 90)
    cr.fill()  
</pre>

<p>
We draw three rectangles of 90x90px size. On two of them, we
perform a scaling operation.
</p>

<pre class="explanation">
cr.scale(0.6, 0.6)
cr.set_source_rgb(0.8, 0.3, 0.2)
cr.rectangle(30, 30, 90, 90)
cr.fill()
</pre>

<p>
We uniformly scale a rectangle by a factor of 0.6.
</p> 

<pre class="explanation">
cr.scale(0.8, 0.8)
cr.set_source_rgb(0.8, 0.8, 0.2)
cr.rectangle(50, 50, 90, 90)
cr.fill()    
</pre>

<p>
Here we perform another scaling operation by a factor of 0.8. 
If we look at the picture, we see, that the third yellow rectangle
is the smallest one. Even if we have used a smaller scaling factor.
This is because transformation operations are additive. In fact, 
the third rectangle was scaled by a factor of 0.528 (0.6x0.8).
</p>

<img src="/img/gfx/pycairo/scaling.png" alt="Scaling operation">
<div class="figure">Figure: Scaling operation</div>


<h2>Isolating transformations</h2>

<p>
Transformation operations are additive. To isolate one operation from
the other one, we can use the <code>save()</code> and <code>restore()</code>
methods. The <code>save()</code> method makes a copy of the current state of the
drawing context and saves it on an internal stack of saved states. 
The <code>restore()</code> method will re-establish the context to the 
saved state.
</p>


<pre class="code">
def on_draw(self, wid, cr):

    cr.set_source_rgb(0.2, 0.3, 0.8)
    cr.rectangle(10, 10, 90, 90)
    cr.fill()
    
    cr.save()
    cr.scale(0.6, 0.6)
    cr.set_source_rgb(0.8, 0.3, 0.2)
    cr.rectangle(30, 30, 90, 90)
    cr.fill()
    cr.restore()
    
    cr.save()
    cr.scale(0.8, 0.8)
    cr.set_source_rgb(0.8, 0.8, 0.2)
    cr.rectangle(50, 50, 90, 90)
    cr.fill()    
    cr.restore()
</pre>

<p>
In the example we scale two rectangles. This time we isolate the 
scaling operations from each other.
</p>

<pre class="explanation">
cr.save()
cr.scale(0.6, 0.6)
cr.set_source_rgb(0.8, 0.3, 0.2)
cr.rectangle(30, 30, 90, 90)
cr.fill()
cr.restore() 
</pre>

<p>
We isolate the scaling operation by putting the <code>scale()</code>
method between the <code>save()</code> and <code>restore()</code> 
methods.
</p>

<img src="/img/gfx/pycairo/isolate.png" alt="Isolating transformations">
<div class="figure">Figure: Isolating transformations</div>

<p>
Now the third yellow rectangle is bigger than the second red one.
</p>


<h2>Donut</h2>

<p>
In the following example we create a complex shape by 
rotating a bunch of ellipses.
</p>


<pre class="code">
#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This program creates a 'donut' shape
in PyCairo.

author: Jan Bodnar
website: zetcode.com 
last edited: August 2012
'''


from gi.repository import Gtk
import cairo
import math


class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()
        
        self.init_ui()
        
        
    def init_ui(self):    

        darea = Gtk.DrawingArea()
        darea.connect("draw", self.on_draw)
        self.add(darea)

        self.set_title("Donut")
        self.resize(350, 250)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        
    
    def on_draw(self, wid, cr):

        cr.set_line_width(0.5)

        w, h = self.get_size()
       
        cr.translate(w/2, h/2)
        cr.arc(0, 0, 120, 0, 2*math.pi)
        cr.stroke()
         
        for i in range(36):
            cr.save()
            cr.rotate(i*math.pi/36)
            cr.scale(0.3, 1)
            cr.arc(0, 0, 120, 0, 2*math.pi)
            cr.restore()
            cr.stroke()
        
    
def main():
    
    app = Example()
    Gtk.main()
        
        
if __name__ == "__main__":    
    main()
</pre>

<p>
We will do rotation and scaling operations. We will also save and restore
PyCairo contexts.
</p>

<pre class="explanation">
cr.translate(w/2, h/2)
cr.arc(0, 0, 120, 0, 2*math.pi)
cr.stroke()
</pre>

<p>
In the middle of the GTK window, we create a circle. This 
will be a bounding circle for our ellipses.
</p>

<pre class="explanation">
for i in range(36):
    cr.save()
    cr.rotate(i*math.pi/36)
    cr.scale(0.3, 1)
    cr.arc(0, 0, 120, 0, 2*math.pi)
    cr.restore()
    cr.stroke()
</pre>

<p>
We create 36 ellipses along the path of our bounding circle. We insulate each
rotate and scale operation from one another with the <code>save()</code> 
and <code>restore()</code> methods.
</p>

<img src="/img/gfx/pycairo/donut.jpg" alt="Donut">
<div class="figure">Figure: Donut</div>


<h2>Star</h2>

<p>
The next example shows a rotating and scaling star.
</p>

<pre class="code">
#!/usr/bin/python

'''
ZetCode PyCairo tutorial 

This is a star example which 
demonstrates scaling, translating and
rotating operations in PyCairo.

author: Jan Bodnar
website: zetcode.com 
last edited: August 2012
'''


from gi.repository import Gtk, GLib
import cairo


class cv(object):

    points = ( 
        ( 0, 85 ), 
        ( 75, 75 ), 
        ( 100, 10 ), 
        ( 125, 75 ), 
        ( 200, 85 ),
        ( 150, 125 ), 
        ( 160, 190 ),
        ( 100, 150 ), 
        ( 40, 190 ),
        ( 50, 125 ),
        ( 0, 85 )
    )

    SPEED = 20
    TIMER_ID = 1


class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()
        
        self.init_ui()
        self.init_vars()
        
        
    def init_ui(self):    

        self.darea = Gtk.DrawingArea()
        self.darea.connect("draw", self.on_draw)
        self.add(self.darea)

        self.set_title("Star")
        self.resize(400, 300)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()
        
        
    def init_vars(self):   
        
        self.angle = 0
        self.scale = 1
        self.delta = 0.01    
        
        GLib.timeout_add(cv.SPEED, self.on_timer)   
        
            
    def on_timer(self):
        
        if self.scale &lt; 0.01:
            self.delta = -self.delta
            
        elif self.scale &gt; 0.99:
            self.delta = -self.delta

        self.scale += self.delta
        self.angle += 0.01    
    
        self.darea.queue_draw()
        
        return True 
    
    
    def on_draw(self, wid, cr):

        w, h = self.get_size()

        cr.set_source_rgb(0, 0.44, 0.7)
        cr.set_line_width(1)

        cr.translate(w/2, h/2)
        cr.rotate(self.angle)
        cr.scale(self.scale, self.scale)

        for i in range(10):
            cr.line_to(cv.points[i][0], cv.points[i][1])
        
        cr.fill()
        
    
def main():
    
    app = Example()
    Gtk.main()
        
        
if __name__ == "__main__":    
    main()
</pre>

<p>
In this example, we create a star object. We will translate it, 
rotate it and scale it. 
</p>

<pre class="explanation">
points = ( 
    ( 0, 85 ), 
    ( 75, 75 ), 
    ( 100, 10 ), 
    ( 125, 75 ), 
    ( 200, 85 ),
...
</pre>

<p>
The star object will be constructed from these points.
</p>

<pre class="explanation">
def init_vars(self):   
    
    self.angle = 0
    self.scale = 1
    self.delta = 0.01  
...
</pre>

<p>
In the init_vars() method, we initialize three variables.
The self.angle is used in the rotation, the self.scale in scaling
the star object. The self.delta variable controls when the star
is growing and when it is shrinking.
</p>

<pre class="explanation">
glib.timeout_add(cv.SPEED, self.on_timer) 
</pre>

<p>
Each cv.SPEED ms the on_timer() method is called.
</p>


<pre class="explanation">
if self.scale &lt; 0.01:
    self.delta = -self.delta
    
elif self.scale &gt; 0.99:
    self.delta = -self.delta
</pre>

<p>
These lines control whether the star is going to grow or shrink.
</p>

<pre class="explanation">
cr.translate(w/2, h/2)
cr.rotate(self.angle)
cr.scale(self.scale, self.scale)
</pre>

<p>
We shift the star into the middle of the window. Rotate it and scale it.
</p>

<pre class="explanation">
for i in range(10):
    cr.line_to(cv.points[i][0], cv.points[i][1])

cr.fill()
</pre>

<p>
Here we draw the star object.
</p>


<p>
In this part of the PyCairo tutorial, we talked about transformations.
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified September 2, 2012  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>


